package com.ruoyi.system.utils;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 分布式锁操作模板默认实现类
 *
 * @author : cfx
 * @date : 2023/9/23 14:51
 **/
@Component
@AllArgsConstructor
@Slf4j
public class SingleDistributedLockTemplate implements DistributedLockTemplate {

    private final RedissonClient redisson;

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, boolean fairLock) {
        return lock(callback, DEFAULT_LEASE_TIME, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
        String lockName = callback.getLockName();
        RLock lock = getLock(lockName, fairLock);
        try {
            lock.lock(leaseTime, timeUnit);
            log.info("[分布式锁操作模板] 加锁 key：[{}] ", lockName);
            return callback.process();
        } finally {
            if (lock != null && lock.isLocked()) {
                lock.unlock();
                log.info("[分布式锁操作模板] 释放锁 key：[{}]  ", lockName);
            }
        }
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock) {
        return tryLock(callback, DEFAULT_WAIT_TIME, DEFAULT_LEASE_TIME, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
        String lockName = callback.getLockName();
        String errorDesc = callback.getErrorDesc();
        RLock lock = getLock(lockName, fairLock);
        try {
            boolean tryLock = lock.tryLock(waitTime, leaseTime, timeUnit);
            log.info("[分布式锁操作模板] 尝试加锁 key：[{}] 加锁状态：[{}] ", lockName, tryLock);
            if (tryLock) {
                return callback.process();
            } else {
                throw new RuntimeException(errorDesc);
            }
        } catch (InterruptedException e) {
            log.info("[分布式锁操作模板] InterruptedException key：[{}]  msg：[{}]", lockName, e.getMessage(), e);
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("[分布式锁操作模板] 释放锁 key：[{}] ", lockName);
            }
        }
        return null;
    }

    private RLock getLock(String lockName, boolean fairLock) {
        RLock lock;
        if (fairLock) {
            lock = redisson.getFairLock(lockName);
        } else {
            lock = redisson.getLock(lockName);
        }
        return lock;
    }

}
